#include "MSFReader.h"

std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
    std::stringstream ss(s);
    std::string item;
    while(std::getline(ss, item, delim)) {
        if(!item.empty()) {
        elems.push_back(item);
        }
    }
    return elems;
}


std::vector<std::string> split(const std::string &s, char delim) {
    std::vector<std::string> elems;
    return split(s, delim, elems);
}


void multipleAlignment::__addNewRef(std::string name, int length) {
    std::string seq;
    std::cout << "Reserving seq " << name << " of length " << length << std::endl;
    seq.reserve(length);
    //_seq.insert(std::pair<std::string, std::string> (name, seq));
    _seq[name]=seq;

}

void multipleAlignment::__appendSeq(std::vector<std::string> elems) {
    std::string name = elems.at(0);
    for(size_t i = 1; i < elems.size(); i++) {
        _seq[name].append(elems.at(i));
    }
}

void multipleAlignment::printAllSeq() {
    for(std::map<std::string, std::string>::iterator it = _seq.begin(); it != _seq.end();  ++it) {
        std::cout << ">" << it->first << std::endl;
        std::cout << it->second << std::endl;
    }
}

void multipleAlignment:: __calculateProfile() {
//    std::map<char,std::vector<int> > counts;
    std::vector<size_t> sums;
    std::map<char, size_t> alphabet;
    
// Initialize profile map
    _counts.resize(_length);

    for (std::map<std::string, std::string>::iterator it = _seq.begin(); 
            it != _seq.end(); ++it) {
        for(size_t i = 0; i < it->second.length(); i++) {
            _counts[i][it->second[i]]++;
//            std::cout << i << " " << it->second[i]<< " " << _counts[i][it->second[i]] << std::endl;
        }
   }

// Iterate through _counts structure
// Also, generate alphabet usage

   for(size_t i = 0; i < _counts.size(); i++) {
        for(std::map<char, size_t>::iterator jt = _counts[i].begin();
                jt != _counts[i].end(); ++jt) {
            alphabet[jt->first] += jt->second;
//            std::cout << jt->first << " " << jt->second << std::endl;
        }
    }
    
// print out alphabet usage just for kicks
// also initialize the _profile map
    for (std::map<char, size_t>::iterator it = alphabet.begin();
            it != alphabet.end(); ++it) {
        _profile[it->first].resize(_length);
        std::cout << it->first << "\t" << it->second <<  std::endl;
    }

// now store everything in _profile for easier access
   for(size_t i = 0; i < _counts.size(); i++) {
        for(std::map<char, size_t>::iterator jt = _counts[i].begin();
                jt != _counts[i].end(); ++jt) {
            _profile[jt->first][i]=jt->second;
        }
    }

    printProfile();
    printConsensus();
}

void multipleAlignment::printProfile() {
    for(std::map<char, std::vector<size_t> >::iterator mapIt = _profile.begin();
            mapIt != _profile.end(); ++mapIt) {
        std::cout << mapIt->first;
        for(std::vector<size_t>::iterator vecIt = mapIt->second.begin();
                vecIt != mapIt->second.end(); ++vecIt) {
            std::cout << " " << *vecIt;
        }
        std::cout << std::endl;
    }
}

void multipleAlignment::printConsensus() {
    std::cout << ">Consensus" << std::endl;
   for(size_t i = 0; i < _counts.size(); i++) {
       char maxChar;
       size_t maxVal=0;
        for(std::map<char, size_t>::iterator jt = _counts[i].begin();
                jt != _counts[i].end(); ++jt) {
            _profile[jt->first][i]=jt->second;
            if (jt->second > maxVal) {
                maxChar = jt->first;
                maxVal = jt->second;
            }
        }
        std::cout << maxChar;
    }
    std::cout << std::endl;


}

void multipleAlignment::setLength(size_t length) {
    _length = length;
}

size_t multipleAlignment::getLength() {
    return _length;
}

void MSFReader::openMSF(std::string filename) {
    std::ifstream file;
    multipleAlignment ref;

    std::cout << "Opening " << filename << std::endl;
    file.open(filename.c_str());
    if (file.is_open()) {
        /*
        // testing functions that can be deleted at some point
        __printHeader(&file);
        __printNextAlignmentBlock(&file);
        __printNextAlignmentBlock(&file);
        */
        
        __parseHeader(&ref, &file);
        __storeAlignments(&ref, &file);
        file.close();
    }

//    ref.printAllSeq();
    ref.__calculateProfile();


}

void MSFReader::__printHeader(std::ifstream *file) {
    std::string line;
    size_t found;
// Read in first 4 lines
    for(int i = 0; i < 4; i++) {
        std::getline(*file, line);
        found=line.find("..");
        if (found != std::string::npos)
            std::cout << "found start of header on " << line << std::endl;
    }


    //    while (file->good()) {
    //        std::getline(*file, line);
    while(std::getline(*file, line)) {
        std::cout << line << std::endl;
        if(line.compare(0,2,"//") == 0) {
            std::cout << "Found end of header" << std::endl;
            break;
        }
        // Here store reference names, their sizes and their check values in 
        // a multiple alignment object
   }
}

void MSFReader::__printNextAlignmentBlock(std::ifstream *file) {
    std::string line;

    while(std::getline(*file, line)) {
        std::cout << line << std::endl;

        if(line.empty()) {
            std::cout << "Found end of alignment block" << std::endl;
            break;
        }
    }
 
}


void MSFReader::__parseHeader(multipleAlignment *align, std::ifstream *file) {
    std::string line;
    size_t found;
    size_t length;
    std::vector<std::string> elems;

    // Read in first 4 lines of msf file that stores some random information
    for(int i = 0; i < 4; i++) {
        std::getline(*file, line);
        found=line.find("..");
        if (found != std::string::npos) {
            std::cout << "found start of header on " << line << std::endl;
            std::vector<std::string> elems = split (line, ' ');
            std::cout << "Setting alignment length to " << elems[1] << std::endl;
            std::istringstream iss(elems[1]);
            iss >> length;
            align->setLength(length);
        }
    }


    //    while (file->good()) {
    //        std::getline(*file, line);
    while(std::getline(*file, line)) {
        if(line.compare(0,2,"//") == 0) {
            std::cout << "Found end of header" << std::endl;
            break;
        }

        // split a header on any number of spaces
        elems = split(line, ' ');

        if (elems[0].compare(0,5,"Name:") == 0) {
            std::cout << "Adding " << elems[1] << " of length " << elems[3] << std::endl;
            std::istringstream iss(elems[3]);
            size_t length;
            iss >> length;

            align->__addNewRef(elems[1],length);
        }

        // Here store reference names, their sizes and their check values in 
        // a multiple alignment object
    }
}


void MSFReader::__storeAlignments(multipleAlignment *align, std::ifstream *file) {
    std::string line;
    std::vector<std::string> elems;

    while(std::getline(*file, line)) {
        if(line.compare(0,2,"//") == 0) {
            std::cout << "Found end of header" << std::endl;
            break;
        }

//        std::cout << line << std::endl;

        elems = split(line, ' ');
        if(elems.size() > 0) {
            align->__appendSeq(elems);
        }
/*
        for(std::vector<std::string>::iterator it = elems.begin(); it !=elems.end() \
                ; ++it) {
            std::cout << *it << std::endl;
        }
        */


    }
}
